// import com.alibaba.druid.sql.ast.SQLCurrentTimeExpr;
// import com.alibaba.druid.sql.ast.SQLCurrentUserExpr;
// import com.alibaba.druid.sql.ast.SQLExpr;
// import com.alibaba.druid.sql.ast.expr.*;
// import com.alibaba.druid.sql.ast.statement.SQLColumnDefinition;
// import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlOutFileExpr;
// import com.alibaba.druid.sql.parser.*;
// import com.alibaba.druid.util.FnvHash;

#include "HiveExprParser.h"
#include "HiveLexer.h"
#include "../../../../utils/FnvHash.h"
#include "../../../../utils/instanceof.h"
#include "../../../../utils/StringUtils.h"
#include "../../../ast/expr/SQLArrayExpr.h"
#include "../../../ast/expr/SQLDecimalExpr.h"
#include "../../../ast/expr/SQLIntervalExpr.h"
#include "../../../ast/expr/SQLIntervalUnit.h"
#include "../../../ast/SQLCurrentTimeExpr.h"
#include "../../../ast/SQLCurrentUserExpr.h"
#include "../../mysql/ast/expr/MySqlOutFileExpr.h"
#include "../../../../utils/log.h"

std::shared_ptr<std::vector<string_ptr>> HiveExprParser::AGGREGATE_FUNCTIONS = std::make_shared<std::vector<string_ptr>>();
std::shared_ptr<std::vector<long>> HiveExprParser::AGGREGATE_FUNCTIONS_CODES = std::make_shared<std::vector<long>>();

BOOL_ptr HiveExprParser::__init = HiveExprParser::init();
BOOL_ptr HiveExprParser::init()
{
  std::shared_ptr<std::vector<string_ptr>> strings = std::make_shared<std::vector<string_ptr>>(std::vector<string_ptr>{
      make_string_ptr("AVG"),
      make_string_ptr("COUNT"),
      make_string_ptr("MAX"),
      make_string_ptr("MIN"),
      make_string_ptr("STDDEV"),
      make_string_ptr("SUM"),
      make_string_ptr("ROW_NUMBER"),
      make_string_ptr("ROWNUMBER")});

  for (size_t i = 0; i < strings->size(); i++)
  {
    AGGREGATE_FUNCTIONS->push_back(make_string_ptr(""));
  }

  AGGREGATE_FUNCTIONS_CODES = FnvHash::fnv1a_64_lower(strings, true);
  // AGGREGATE_FUNCTIONS = new string_ptr[AGGREGATE_FUNCTIONS_CODES.length];
  for (string_ptr str : *strings)
  {
    long hash = FnvHash::fnv1a_64_lower(str);
    // int index = Arrays.binarySearch(AGGREGATE_FUNCTIONS_CODES, hash);
    int index = -1;
    int size = strings->size();
    for (int i = 0; i < size; i++)
    {
      // LOG_INFO << __FUNCTION__ << ": i:" << i << ",size:" << size;
      if (HiveExprParser::AGGREGATE_FUNCTIONS_CODES->at(i) == hash)
      {
        index = i;
        break;
      }
    }

    // LOG_INFO << "index:" << index;
    // LOG_INFO << "AGGREGATE_FUNCTIONS->size():" << AGGREGATE_FUNCTIONS->size();
    // LOG_INFO << "str:" << str;
    AGGREGATE_FUNCTIONS->at(index) = str;
  }
}

HiveExprParser::HiveExprParser(string_ptr sql)
    : HiveExprParser(HiveLexer_ptr(new HiveLexer(sql)))
{

  this->lexer->nextToken();
}

HiveExprParser::HiveExprParser(string_ptr sql, SQLParserFeature_list_ptr features)
    : HiveExprParser(HiveLexer_ptr(new HiveLexer(sql, features)))
{
  this->lexer->nextToken();
}

HiveExprParser::HiveExprParser(Lexer_ptr lexer)
    : SQLExprParser(lexer)
{
  this->aggregateFunctions = AGGREGATE_FUNCTIONS;
  this->aggregateFunctionHashCodes = AGGREGATE_FUNCTIONS_CODES;
}

SQLExpr_ptr HiveExprParser::primaryRest(SQLExpr_ptr expr)
{
  //        if(lexer->token() == Token::COLON) {
  //            lexer->nextToken();
  //            expr = dotRest(expr);
  //            return expr;
  //        }

  if (*lexer->token() == Token::LBRACKET)
  {
    SQLArrayExpr_ptr array = SQLArrayExpr_ptr(new SQLArrayExpr());
    array->setExpr(expr);
    lexer->nextToken();
    this->exprList(array->getValues(), array);
    accept(Token::RBRACKET);
    return primaryRest(array);
  }
  else if (*lexer->token() == Token::LITERAL_CHARS)
  {
    if (instanceof <SQLExpr, SQLCharExpr>(expr))
    {
      string_ptr text2 = std::dynamic_pointer_cast<SQLCharExpr>(expr)->getText();
      do
      {
        string_ptr chars = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
        text2->append(chars->c_str());
        lexer->nextToken();
      } while (*lexer->token() == Token::LITERAL_CHARS || *lexer->token() == Token::LITERAL_ALIAS);
      expr = SQLCharExpr_ptr(new SQLCharExpr(text2));
    }
  }
  else if (*lexer->token() == Token::IDENTIFIER)
  {
    if (lexer->identifierEquals(FnvHash::Constants::BD) &&
        instanceof <SQLExpr, SQLNumericLiteralExpr>(expr))
    {
      lexer->nextToken();
      double num = (std::dynamic_pointer_cast<SQLNumericLiteralExpr>(expr))->getNumber();
      expr = std::dynamic_pointer_cast<SQLExpr>(SQLDecimalExpr_ptr(new SQLDecimalExpr(make_string_ptr(std::to_string(num)))));
    }
    else if (lexer->identifierEquals(FnvHash::Constants::DAYS))
    { // hortonworks
      lexer->nextToken();
      SQLIntervalExpr_ptr intervalExpr = SQLIntervalExpr_ptr(new SQLIntervalExpr());
      intervalExpr->setValue(expr);
      intervalExpr->setUnit(SQLIntervalUnit_ptr(new SQLIntervalUnit(&SQLIntervalUnit::DAY)));
    }
  }

  return SQLExprParser::primaryRest(expr);
}

SQLExpr_ptr HiveExprParser::primary()
{
  Token_ptr tok = lexer->token();
  if (*tok == Token::IDENTIFIER)
  {
    long hash_lower = lexer->hashLCase();
    if (hash_lower == FnvHash::Constants::OUTLINE)
    {
      lexer->nextToken();
      SQLExpr_ptr file = primary();
      SQLExpr_ptr expr = SQLExpr_ptr(new MySqlOutFileExpr(file));

      return primaryRest(expr);
    }

    SQLCurrentTimeExpr_ptr currentTimeExpr = nullptr;
    if (hash_lower == FnvHash::Constants::CURRENT_TIMESTAMP)
    {
      currentTimeExpr = SQLCurrentTimeExpr_ptr(new SQLCurrentTimeExpr(SQLCurrentTimeExpr_Type_ptr(new SQLCurrentTimeExpr_Type(SQLCurrentTimeExpr_Type::CURRENT_TIMESTAMP.name))));
    }
    else if (hash_lower == FnvHash::Constants::CURRENT_DATE)
    {
      currentTimeExpr = SQLCurrentTimeExpr_ptr(new SQLCurrentTimeExpr(SQLCurrentTimeExpr_Type_ptr(new SQLCurrentTimeExpr_Type(SQLCurrentTimeExpr_Type::CURRENT_DATE))));
    }
    else if (hash_lower == FnvHash::Constants::CURRENT_USER && isEnabled(SQLParserFeature_ptr(new SQLParserFeature(&SQLParserFeature::EnableCurrentUserExpr))))
    {
      lexer->nextToken();
      return primaryRest(SQLCurrentUserExpr_ptr(new SQLCurrentUserExpr()));
    }

    if (currentTimeExpr != nullptr)
    {
      string_ptr methodName = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
      lexer->nextToken();

      if (*lexer->token() == Token::LPAREN)
      {
        lexer->nextToken();
        if (*lexer->token() == Token::LPAREN)
        {
          lexer->nextToken();
        }
        else
        {
          return primaryRest(methodRest(SQLIdentifierExpr_ptr(new SQLIdentifierExpr(methodName)), BOOL::FALSE));
        }
      }

      return primaryRest(currentTimeExpr);
    }
  }

  return SQLExprParser::primary();
}

SQLExpr_ptr HiveExprParser::parseAliasExpr(string_ptr alias)
{
  string_ptr chars = make_string_ptr(alias->substr(1, alias->length() - 1));

  string_ptr buf = nullptr;

  for (int i = 0; i < chars->length(); ++i)
  {
    char ch = chars->at(i);
    if (ch == '\\' && i < chars->length() - 1)
    {
      char next = chars->at(i + 1);
      if (next == '\\')
      {
        if (buf == nullptr)
        {
          buf = make_string_ptr("");
          buf->append(chars->substr(0, i));
        }
        buf->append("\\");
        ++i;
      }
      else if (next == '\"')
      {
        if (buf == nullptr)
        {
          buf = make_string_ptr("");
          buf->append(chars->substr(0, i));
        }
        buf->append("\"");
        ++i;
      }
      else
      {
        if (buf != nullptr)
        {
          buf->append(StringUtils::charToString(ch));
        }
      }
    }
    else
    {
      if (buf != nullptr)
      {
        buf->append(StringUtils::charToString(ch));
      }
    }
  }

  if (buf != nullptr)
  {
    chars = buf;
  }
  return SQLCharExpr_ptr(new SQLCharExpr(chars));
}

SQLColumnDefinition_ptr HiveExprParser::parseColumnRest(SQLColumnDefinition_ptr column)
{
  if (lexer->identifierEquals(FnvHash::Constants::MAPPED))
  {
    lexer->nextToken();
    accept(Token::BY);
    this->parseAssignItem(column->getMappedBy(), column);
  }

  if (lexer->identifierEquals(FnvHash::Constants::COLPROPERTIES))
  {
    lexer->nextToken();
    this->parseAssignItem(column->getColProperties(), column);
  }

  return SQLExprParser::parseColumnRest(column);
}

SQLExpr_ptr HiveExprParser::parseInterval()
{
  accept(Token::INTERVAL);
  SQLExpr_ptr value = expr();

  if (*lexer->token() != Token::IDENTIFIER)
  {
    throw new ParserException(make_string_ptr("Syntax error. " + encode_util::UnicodeToUTF8(lexer->info())));
  }

  string_ptr unit_ = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
  lexer->nextToken();

  SQLIntervalExpr_ptr intervalExpr = SQLIntervalExpr_ptr(new SQLIntervalExpr());
  intervalExpr->setValue(value);
  SQLIntervalUnit_ptr intervalUnit = SQLIntervalUnit::valueOf(StringUtils::toUpper(unit_));
  if (*intervalUnit == SQLIntervalUnit::YEAR && *lexer->token() == Token::TO)
  {
    lexer->nextToken();
    acceptIdentifier("MONTH");
    intervalUnit = SQLIntervalUnit_ptr(new SQLIntervalUnit(&SQLIntervalUnit::YEAR_TO_MONTH));
  }
  intervalExpr->setUnit(intervalUnit);

  return intervalExpr;
}
